#include <library.h>
#include "../../os_host/source/framework/BufferedIo.h"
using namespace BIOS;

#include "stdfunc.h"
#include "promise.h"
#include "stream.h"
#include "atdevice.h"
//#include "tokenizer.h" // TODO: remove?
#include "esp8266.h"

class CServerApp
{
    CEsp8266 mDev;
    CPromise mProcess;
    static CServerApp* pInstance;

public:
    CServerApp()
    {
        pInstance = this;
    }
    
    void InitInquiry()
    {
        /*.Then([](){ return CEsp8266::Instance().WifiJoin("*****", "*********"); })
         .Then([](){ return CEsp8266::Instance().Inquiry([](char* ssid, int rssi, bool open)
         {
         fprintf(stdout, "Got wifi: ssid=%s, rssi=%d, open=%d\n", ssid, rssi, open);
         });})*/
    }
    
    static void FileLister(char* path, CDataStream& stream)
    {
        stream << "<html>\n<h1>Listing folder " << path << "</h1>\n";
        
        char fixPath[64];
        if (strlen(path) > 2)
        {
            strncpy(fixPath, path+1, strlen(path)-2);
            fixPath[strlen(path)-2] = 0;
        }
        else
            strcpy(fixPath, "");

        // first char should be '/'
        FAT::EResult eOpen = FAT::OpenDir(fixPath);
        if (eOpen == FAT::EResult::EOk)
        {
            // Folders first
            FAT::TFindFile file;
            while (FAT::FindNext(&file) == FAT::EResult::EOk)
            {
                if (file.strName[0] == '.')
                    continue;
                
                if (file.nAtrib & FAT::EAttribute::EDirectory)
                {
                    stream << "<a href=\"" << path << file.strName << "/\">";
                    stream << "&lt;" << file.strName << "&gt;";
                    stream << "</a>";
                    stream << "<br>\n";
                }
            }
            
            // files next
            eOpen = FAT::OpenDir(fixPath);
            while (FAT::FindNext(&file) == FAT::EResult::EOk)
            {
                if (file.strName[0] == '.')
                    continue;

                if (!(file.nAtrib & FAT::EAttribute::EDirectory))
                {
                    stream << "<a href=\"" << path << file.strName << "\">";
                    for (int i=0; i<(int)strlen(file.strName); i++)
                        file.strName[i] = tolower(file.strName[i]);
                    stream << file.strName;
                    stream << "</a>";
                    stream << "<br>\n";
                }
            }
        } else
        {
            stream << "Path not found";
        }
        stream << "<hr><i>Generated by ESP8266 on LA104</i>";
        stream << "</html>";
    }
    
    static void FileDump(char* path, CDataStream& stream)
    {
        CBufferedReader f;
        f.Open(path+1);
        int len = f.GetFileSize();
        
        char str[2] = {0, 0};
        for (int i=0; i<len; i++)
        {
            uint8_t b;
            f >> b;
            str[0] = b;
            stream << str;
        }
        f.Close();
    }

    void InitServer()
    {
        DBG::Print("Starting server...\n");
        static const char* wifiSsid = "LA104";
        static const char* wifiPassword = "12345678";
        
        mProcess = mDev.Reset()
        .Then([](){ return CEsp8266::Instance().SetModeAp(); })
        .Then([](){ return CEsp8266::Instance().WifiCreate(wifiSsid, wifiPassword); })
        .Then([](){ return CEsp8266::Instance().ServerCreate(
            [](int id, char* method, char* path, char* query)
            {
                DBG::Print("Request '%s'...", path);

                static char _path[64];
                if (strlen(path) < 63)
                    strcpy(_path, path);
                else
                    strcpy(_path, "");

                auto respNotFound = [](CDataStream& stream)
                {
                    stream << "HTTP/1.1 404 Not Found\r\n";
                };
                
                auto respLister = [](CDataStream& stream)
                {
                    stream << "HTTP/1.1 200 OK\r\n\r\n";
                    FileLister(_path, stream);
                };

                auto respFile = [](CDataStream& stream)
                {
                    stream << "HTTP/1.1 200 OK\r\n\r\n";
                    FileDump(_path, stream);
                };

                static CEsp8266::TMessageGenerator generator;
                if (strcmp(path, "/favicon.ico") == 0)
                    generator = respNotFound;
                else if (path[strlen(path)-1] == '/')
                    generator = respLister;
                else
                    generator = respFile;

                static int _id;
                _id = id;
                
                if (pInstance->mProcess.Finished())
                {
                    pInstance->mProcess = CEsp8266::Instance().ServerResponse(_id, generator)
                    .Then([]() { DBG::Print("Ok\n"); return CPromise::Resolve(); })
                    .Catch([](){
                        DBG::Print("Failed\n");
                        fprintf(stdout, "Failed to send response :(\n");
                        return CPromise::Resolve(); });
                } else
                {
                    DBG::Print("Failed\n");
                    fprintf(stdout, "Unable to handle request now\n");
                }
            }); })
        .Then([](){
            DBG::Print("Web server running\n");
            DBG::Print("Ssid:%s, Password:%s\n", wifiSsid, wifiPassword);
            DBG::Print("Open 192.168.4.1 in browser\n");
            return CPromise::Resolve(); })
        .Catch([](){
            DBG::Print("Web server cannot start\n");
            return CPromise::Resolve(); });
    }
    
    bool Do()
    {
        while (mDev.Transfer())
        {
            if (!mProcess.Process())
                return false;
        }

        EVERY(100)
        {
            if (!mProcess.Process())
                return false;
        }
        return true;
    }
};

CServerApp* CServerApp::pInstance = nullptr;
CServerApp app;

#ifdef _ARM
__attribute__((__section__(".entry")))
#endif
int _main(void)
{
    CRect rcClient(0, 0, LCD::Width, LCD::Height);
    LCD::Bar(rcClient, RGB565(0000b0));
    
    CRect rc1(rcClient);
    rc1.bottom = 14;
    GUI::Background(rc1, RGB565(4040b0), RGB565(404040));
    
    BIOS::LCD::Print(8, 0, RGB565(ffffff), RGBTRANS, "ESP8266 web server");
    DBG::Print("$\n");
    
    app.InitServer();
    BIOS::KEY::EKey key;
    while ((key = KEY::GetKey()) != KEY::EKey::Escape)
    {
        app.Do();
    }
    
    return 0;
}

void _HandleAssertion(const char* file, int line, const char* cond)
{
    BIOS::DBG::Print("Assertion failed in ");
    BIOS::DBG::Print(file);
    BIOS::DBG::Print(") [%d]: %s\n", line, cond);
#ifdef __APPLE__
    kill(getpid(), SIGSTOP);
#endif
    while (1);
}

